﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace EarthGrid.core
{
    public class Grid
    {
        /// <summary>
        /// 1弧度单位，1°对应的弧度为一个单位
        /// </summary>
        public static double R_1单位弧度 = Math.PI / 180;

        private int level;

        /// <summary>
        /// 网格级别，总共31个，从1开始，0级是未划分的不用考虑
        /// </summary>
        public int Level { get => level; set => level = value; }
        
        private string code;

        /// <summary>
        /// 网格椭球面的编码
        /// </summary>
        public string Code { get => code; set => code = value; }

        #region 网格四至范围，在扩展的基础上，取值在正负256°之间
        private LonLat minLon;
        private LonLat maxLon;
        private LonLat minLat;
        private LonLat maxLat;

        private double rad;
        private int n_所在层;
        private double h_网格自身高;
        private double r_网格距地心距离;
        private double l_网格底边长;
        private double h_网格距地面高;
        private string h_code;

        public LonLat MinLon { get => minLon; set => minLon = value; }
        public LonLat MaxLon { get => maxLon; set => maxLon = value; }
        public LonLat MinLat { get => minLat; set => minLat = value; }
        public LonLat MaxLat { get => maxLat; set => maxLat = value; }

        /// <summary>
        /// 地面以上从0开始的层数，地面以下从-1开始
        /// </summary>
        public int N_所在层 { get => n_所在层; set => n_所在层 = value; }
        public double Rad_弧度 { get => get弧度(); set => rad = value; }

        public double H_网格自身高 { get => h_网格自身高; set => h_网格自身高 = value; }
        public double R_网格距地心距离 { get => r_网格距地心距离; set => r_网格距地心距离 = value; }
        public double L_网格底边长 { get => l_网格底边长; set => l_网格底边长 = value;  }

        /// <summary>
        /// 负的表示网格在地下
        /// </summary>
        public double H_网格距地面高 { get => h_网格距地面高; set => h_网格距地面高 = value; }

        /// <summary>
        /// 网格的高度方向编码，地面以上从0开始，地面以下从1开始
        /// </summary>
        public string H_Code { get => h_code; set => h_code = value; }
        #endregion

        public Hemisphere Hemisphere()
        {
            switch (code.Substring(0,2))
            {
                case "G0":
                    return core.Hemisphere.G0;
                case "G1":
                    return core.Hemisphere.G1;
                case "G2":
                    return core.Hemisphere.G2;
                case "G3":
                    return core.Hemisphere.G3;
                default:
                    return core.Hemisphere.G0;
            }

        }

        #region 初始化了第一级别的地上0层的网格
        public static Grid FirstLevelGridG0 = new Grid(1, "G0", 
            new LonLat(LonLatEnum.lon, 0, 0, 0), new LonLat(LonLatEnum.lat, 0, 0, 0),
            new LonLat(LonLatEnum.lon, 256, 0, 0), new LonLat(LonLatEnum.lat, 256, 0, 0));
        public static Grid FirstLevelGridG1 = new Grid(1, "G1",
            new LonLat(LonLatEnum.lon, -256, 0, 0), new LonLat(LonLatEnum.lat, 0, 0, 0),
            new LonLat(LonLatEnum.lon, 0, 0, 0), new LonLat(LonLatEnum.lat, 256, 0, 0));
        public static Grid FirstLevelGridG2 = new Grid(1, "G2",
            new LonLat(LonLatEnum.lon, 0, 0, 0), new LonLat(LonLatEnum.lat, -256, 0, 0),
            new LonLat(LonLatEnum.lon, 256, 0, 0), new LonLat(LonLatEnum.lat, 0, 0, 0));
        public static Grid FirstLevelGridG3 = new Grid(1, "G3",
            new LonLat(LonLatEnum.lon, -256, 0, 0), new LonLat(LonLatEnum.lat, -256, 0, 0),
            new LonLat(LonLatEnum.lon, 0, 0, 0), new LonLat(LonLatEnum.lat, 0, 0, 0));
        
       
        /// <summary>
        /// 初始化第一级网格，总共31级，第9级时，地上256层，地下256层
        /// </summary>
        /// <param name="level"></param>
        /// <param name="code"></param>
        /// <param name="minLon"></param>
        /// <param name="minLat"></param>
        /// <param name="maxLon"></param>
        /// <param name="maxLat"></param>
        private Grid(int level, string code, LonLat minLon, LonLat minLat, LonLat maxLon, LonLat maxLat)
        {
            this.level = level;
            this.code = code;
            this.minLon = minLon;
            this.minLat = minLat;
            this.maxLon = maxLon;
            this.maxLat = maxLat;
            // 地上网格层从0开始，地下从-1开始
            this.n_所在层 = 0;
            if (level == 1)
            {
                this.h_code = "0";
                this.Rad_弧度 = 256 * Math.PI / 180;
            }
        }
        #endregion

        public Grid() { }

        /// <summary>
        /// 单个生成下级4个或8个，同时给level自动赋值
        /// </summary>
        /// <param name="立体"></param>
        /// <returns>返回8个时，0-3是下层四个网格，4-7是上层四个，04上下，15上下，26上下，37上下</returns>
        public Grid[] InitNextLevel(bool 立体)
        {
            Grid g1 = new Grid
            {
                Level = level + 1
            };
            Grid g2 = new Grid
            {
                level = level + 1
            };
            Grid g3 = new Grid
            {
                level = level + 1
            };
            Grid g4 = new Grid
            {
                level = level + 1
            };
            if (立体)
            {
                Grid g11 = g1.Copy();
                Grid g21 = g2.Copy();
                Grid g31 = g3.Copy();
                Grid g41 = g4.Copy();
                if (n_所在层 > 0)
                {
                    g1.n_所在层 = g2.n_所在层 = g3.n_所在层 = g4.n_所在层 = n_所在层 * 2;
                    g11.n_所在层 = g21.n_所在层 = g31.n_所在层 = g41.n_所在层 = g1.n_所在层 + 1;
                }
                else
                { 
                    g1.n_所在层 = g2.n_所在层 = g3.n_所在层 = g4.n_所在层 = n_所在层 * 2 + 1;
                    g11.n_所在层 = g21.n_所在层 = g31.n_所在层 = g41.n_所在层 = n_所在层 * 2;
                }
                g1.h_code = g2.h_code = g3.h_code = g4.h_code = h_code + "0";
                g11.h_code = g21.h_code = g31.h_code = g41.h_code = h_code + "1";
                return new Grid[] { g1, g2, g3, g4, g11, g21, g31, g41};
            }
            return new Grid[] { g1 , g2, g3, g4};
        }

        /// <summary>
        /// 给定左下和右上两点经纬度
        /// </summary>
        /// <param name="LD_LonLat"></param>
        /// <param name="RU_LonLat"></param>
        public void SetBox(Point LD, Point RU)
        {
            MinLon = LD.Lon;
            MinLat = LD.Lat;
            MaxLon = RU.Lon;
            MaxLat = RU.Lat;
        }

        public void SetBox(LonLat minLon, LonLat minLat, LonLat maxLon, LonLat maxLat)
        {
            this.minLon = minLon;
            this.minLat = minLat;
            this.maxLon = maxLon;
            this.maxLat = maxLat;
        }

        public String GetGridCode()
        {
            return code.Substring(0, level + 1);
        }

        public Point GetCenter()
        {
            LonLat lon = new LonLat();
            LonLat lat = new LonLat();
            
            lon.Type = LonLatEnum.lon;
            lat.Type = LonLatEnum.lat;

            lon.Du = (minLon.Du + maxLon.Du) / 2;
            lon.Fen = (minLon.Fen + maxLon.Fen) / 2;
            lon.Miao = (minLon.Miao + maxLon.Miao) / 2;

            lat.Du = (minLat.Du + maxLat.Du) / 2;
            lat.Fen = (minLat.Fen + maxLat.Fen) / 2;
            lat.Miao = (minLat.Miao + maxLat.Miao) / 2;

            return new Point(lon, lat);
        }
        
        public LevelEnum LevelEnum()
        {
            if (level < 10)
            {
                return core.LevelEnum.du;
            }
            else if (level < 16)
            {
                return core.LevelEnum.fen;
            } 
            else if (level < 22)
            {
                return core.LevelEnum.miao;
            }
            else
            {
                return core.LevelEnum.xiaoshu;
            }
        }

        /// <summary>
        /// 判断网格是否为有效网格，因为扩展到256度，64分，64秒的时候，分割后有些网格不存在就不必再分割
        /// </summary>
        /// <returns></returns>
        public bool 是否有效()
        {
            switch (LevelEnum())
            {
                case core.LevelEnum.du:
                    int mindu = minLon.Du < -180 ? -180 : minLon.Du;
                    int maxdu = maxLon.Du > 180 ? 180 : maxLon.Du;
                    return mindu < maxdu && (minLat.Du < -90 ? -90 : minLat.Du) < (maxLat.Du > 90 ? 90 : maxLat.Du);
                case core.LevelEnum.fen:
                    int minfen = minLon.Fen < -60 ? -60 : minLon.Fen;
                    int maxfen = maxLon.Fen > 60 ? 60 : maxLon.Fen;
                    return minfen < maxfen && (minLat.Fen < -60 ? -60 : minLat.Fen) < (maxLat.Fen > 60 ? 60 : maxLat.Fen);
                case core.LevelEnum.miao:
                    // TODO 可能有问题导致有无效网格
                    return (minLon.Miao < -60 ? -60 : minLon.Miao) < (maxLon.Miao > 60 ? 60 : minLon.Miao) 
                        && (minLat.Miao < -60 ? -60 : minLat.Miao) < (maxLat.Miao > 60 ? 60 : maxLat.Miao);
                default:
                    return false;
            }
        }

        /// <summary>
        /// 只复制了网格的 四个经纬度坐标、level级别、球面code和弧度
        /// </summary>
        /// <returns></returns>
        public Grid Copy()
        {
            Grid g = new Grid
            {
                Code = Code,
                Rad_弧度 = Rad_弧度,
                level = level,
                minLon = minLon,
                maxLon = maxLon,
                minLat = minLat,
                maxLat = maxLat
            };
            return g;
        }

        public double get弧度()
        {
            if (rad > 0)
                return rad;
            rad = (maxLat.to十进制() - minLat.to十进制()) * R_1单位弧度;
            return rad;
        }
    }
}
